/***************************************************************************************************
  *Copyright(C),2010-2016,Sumscope
  *FileName	:  string_page.cpp
  *Author	:  scofined.qi
  *Version	:  1.0
  *Date		:  2017/05/27
  *Desc		:  //用于主要说明此程序文件完成的主要功能
  *Relation :  
  *Others	:  //其他内容说明
  *Function :  //主要函数列表，每条记录应包含函数名及功能简要说明
  *History	:  //修改历史记录列表，每条修改记录应包含修改日期、修改者及修改内容简介
***************************************************************************************************/
#include "string_page.h"
#include <string.h>
#include <vector>
#include <list>
#include "../allocator/allocator_pool_manager.h"

#define RTL_FIELD_SIZE(type,field)  sizeof(reinterpret_cast<type *>(0)->field)
#define BASSERTS //;

namespace qb{
	namespace base{

		namespace sp{
			class SStringItem{
				typedef StringPage::Element Element;
				typedef StringPage::ConstElementPtr ConstElementPtr;
			private:
				unsigned short			m_magic;			// 固定的值，便于纠错和调试
				unsigned short			m_id;				// id，序号
				unsigned short 			m_len;				// 实际字符串的长度
				unsigned short			m_bytes;			// 当前类所占据的字节长度
				SStringItem*	m_next;				// 
				Element			m_string[4];		// [m_bytes/4]+4 length
			public:
				const Element* GetString()const{ return m_string; }
				static int PredicateSize(int len)
				{
					// 预估len个字符长度的字符串所需要占据的空间大小，包括SStringItem大小
					// 另外要考虑4字节对齐
					int thissize = sizeof(SStringItem) - RTL_FIELD_SIZE(SStringItem, m_string);
					int num = len*sizeof(Element) / 4;
					int size = num * 4 + 4;
					size = size + thissize;
					BASSERTS(size > len, "");
					return size;
				}
				void SetNext(SStringItem* next){ 
					m_next = next; 
				}
				SStringItem* GetNext()const{ return m_next; }
				void Construct(ConstElementPtr str, int len, int size, int id)
				{
					// size 是字节数，len是字符数
					m_magic = StringPage::MAGIC;
					m_id = id;
					m_len = len;
					m_bytes = size;
					m_next = nullptr;
					//memset(this, 0, size); // 这行是多余的，SStringBlock构建的时候就全部设为0了
					memcpy(m_string, str, len*sizeof(Element));
					m_string[len] = Element(0);
				}
				void Check(int id, SStringItem* next)
				{
					BASSERTS(m_magic == StringPage::MAGIC, "");
					BASSERTS(m_id == id, "");
					BASSERTS(m_len == sizeof(Element) == 1 ? strlen((char*)m_string) : wcslen((wchar_t*)m_string), "");
					BASSERTS(m_next == next, "");
					BASSERTS((char*)this + m_bytes == (char*)next, "");
				}
			};
			class SStringBlock{
				typedef StringPage::Element Element;
				typedef StringPage::ConstElementPtr ConstElementPtr;
				int			m_capcity;				// m_buffer 实际所分配的字节数
				int			m_really_used;			// 该block所拥有的字符串的总的长度，不包括末尾nullptr
				int			m_used;					// 下一个SStringItem的开头指针相对于m_buffer的偏移量
				int			m_count;				// 当前拥有的SStringItem的个数
				Element*	m_buffer;				// 实际分配的内存,由外部负责处理
				SStringItem*	m_current;			// 记录最近一次加入的SStringItem指针
			public:
				SStringBlock(Element* buffer, int capcity) :
					m_buffer(buffer),
					m_capcity(capcity),
					m_used(0),
					m_really_used(0),
					m_count(0),
					m_current(nullptr)
				{
					BASSERTS(capcity >= 1024, "SStringBlock.m_capcity can not be smaller than 1024");
					memset(m_buffer, 0, capcity*sizeof(Element));
				}
				~SStringBlock()
				{
					m_buffer = nullptr;
				}
				void Reset()
				{
					m_used=m_really_used = 0;
					m_count = 0;
					m_current = nullptr;
					memset(m_buffer, 0, m_capcity*sizeof(Element));
				}
				ConstElementPtr AddString(ConstElementPtr str, int len, int size)
				{
					BASSERTS(m_buffer != nullptr, "");
					BASSERTS(m_capcity > 0 && m_used >= 0 && m_used <= m_capcity, "");
					if (m_used + size > m_capcity)
						return nullptr;

					// m_used 是字节数
					Element* ptr = (Element*)((char*)m_buffer + m_used);
					SStringItem* item = (SStringItem*)(ptr);
					item->Construct(str, len, size, m_count);

					BASSERTS(len < size, "");

					if (m_current)
						m_current->SetNext(item);

					m_current = item;
					m_count++;
					m_used += size;
					m_really_used += len*sizeof(Element);

					//DVIEW("Add String:item:%x old:%s str:%s used:%d count:%d",ptr,str,item->GetString(),m_used,size);
					return item->GetString();
				}
				void Check()
				{
					SStringItem* item = (SStringItem*)m_buffer;
					SStringItem* next = nullptr;
					for (int i = 0; i < m_count; i++)
					{
						next = item->GetNext();
						item->Check(i, next);
						item = next;
					}

				}
			};
			class SStringBlockList{
				int						m_block_size;
				std::vector<SStringBlock*>	m_blocks;		// address increase 
				std::list<SStringBlock*>		m_free_blocks;	// 
				HeapAllocator			m_heap;
			public:
				SStringBlockList(int blockSize) :m_block_size(blockSize),m_heap("StringPage"){}
				~SStringBlockList()
				{
					//MEM_METRIC("~SStringBlockList,count:%d,size:%d",m_blocks.size()+m_free_blocks.size(),m_block_size);
					Clear();
				}
				void DumpStatus()
				{
					//BLOGDEBUG("[MEMORY]free:%6d  cur:%6d block_size:%6d",
						//m_free_blocks.size(), m_blocks.size(), m_block_size);
				}
				void Reset()
				{
					int count = m_blocks.size();
					for (int i = 0; i < count; i++)
					{
						SStringBlock* block = m_blocks.at(i);
						m_free_blocks.push_back(block);
					}
					m_blocks.clear();
				}
				void Clear()
				{
					Reset();
					for (auto it = m_free_blocks.begin(); it != m_free_blocks.end();++it)
					{
						SStringBlock* block = *it;
						m_heap.Free(block,0);
					}
					m_free_blocks.clear();
					m_heap.Reset();
				}
				int GetBytes()const
				{
					return m_blocks.size()*m_block_size;
				}
				SStringBlock* GetLastBlock()
				{
					if (m_blocks.size() > 0)
						return m_blocks.at(m_blocks.size() - 1);
					return nullptr;
				}
				SStringBlock* GetNewBlock()
				{
					SStringBlock* block = nullptr;
					if (!m_free_blocks.empty())
					{
						block=m_free_blocks.front();
						m_free_blocks.pop_front();
						block->Reset();
					}
					else
					{
						int capcity = m_block_size*sizeof(StringPage::Element);
						StringPage::Element* buffer = (StringPage::Element*)m_heap.Alloc(capcity);
						block = new (m_heap.Alloc(sizeof(SStringBlock))) SStringBlock(buffer, m_block_size);
					}
					m_blocks.push_back(block);
					return block;
				}
			};
		}

		StringPage::StringPage() :m_block_size(4096 * 4), m_heap(nullptr)
		{
			m_block_list = new SStringBlockList(m_block_size);
		}

		StringPage::~StringPage()
		{
			Clear();
			if (m_block_list)
				delete m_block_list;
		}

		void StringPage::Reset()
		{
			if (m_block_list)
				m_block_list->Reset();
		}
		void StringPage::Clear()
		{
			if (m_block_list)
				m_block_list->Clear();
		}
		qb::base::StringPage::ConstElementPtr StringPage::AddString(ConstElementPtr str, int len)
		{
			BASSERTS(str != nullptr, "");
			BASSERTS(len < 1024, "SStringBuf Can not support string whose size is larger than 1024,%d",len);

			SStringBlock* block = m_block_list->GetLastBlock();
			int item_size = sp::SStringItem::PredicateSize(len);
			ConstElementPtr str_add = block ? block->AddString(str, len, item_size) : nullptr;
			if (str_add == nullptr)
			{
				block = m_block_list->GetNewBlock();
				str_add = block->AddString(str, len, item_size);
			}

			BASSERTS(str_add != nullptr, "");
			return str_add;
		}

		int StringPage::GetBytes()
		{
			return m_block_list->GetBytes();
			
		}
		void StringPage::DumpStatus()
		{
			if (m_block_list)
				m_block_list->DumpStatus();
		}
	}
}